
#ifndef _TTG_PIPELINE_NODES_PROCESSOR_NODE_
#define _TTG_PIPELINE_NODES_PROCESSOR_NODE_
#include "Common/Definitions.h"

#include "vector"
#include "list"

#include "Common/Structures.h"
#include "NavigatorNode.h"
#include "ParametrizedOutput.h"
#include "ParametrizedInput.h"
#include "IDataProcessor.h"
#include "TaskKernels.h"

namespace ttg
{
	namespace pipeline
	{
		//Version of SubPipeline for user.
		template <class InType, class OutType>
		class SubPipeline
		{
			private:
				ttg_internal::pipeline::ISubPipeline *real_sub_pipeline;
			//fix me
			//private:
			public:
				//Creates wrapper for real sub pipeline.
				inline SubPipeline(ttg_internal::pipeline::ISubPipeline *real_sub_pipeline)
				{ this->real_sub_pipeline = real_sub_pipeline; }
				//Returns wrapped sub pipeline.
				inline ttg_internal::pipeline::ISubPipeline *&getSubPipeline()
				{ return real_sub_pipeline; }
			public:
				//Creates uninitialized SubPipeline. Use "createSubPipeline" to init.
				inline SubPipeline()
				{ this->real_sub_pipeline = NULL; }
		};
	}
}

namespace ttg_internal
{
	namespace pipeline
	{
		//Object, that executes subpipeline and notifies owner about its finish.
		template <class ProcessInType,
				  class InType, class OutType,
				  class Object>
		class SubPipelineExecutor
		{
			private:
				Object *owner;
				ttg::structures::Delegate<void, std::vector<OutType> &> *d;
				ttg::pipeline::ContextID cur_ID;
			public:
				inline SubPipelineExecutor(ISubPipeline *sub_pipeline,
										   ttg::structures::Enumerator<InType> &data,
										   Object *owner,
										   ttg::structures::Delegate<void, std::vector<OutType> &> *d,
										   ttg::pipeline::ContextID ID)
				{
					this->owner = owner;
					this->d = d;
					this->cur_ID = ID;
					//Incing task count.
					NodeState &state = owner->getState(cur_ID);
					state.getLocker().enter();
					state.getActiveTaskCount() += 1;
					state.getLocker().leave();
					NodeInfo &info = owner->getInfo();
					info.getLocker().enter();
					info.getActiveTaskCount() += 1;
					info.getLocker().leave();
					//Starting.
					sub_pipeline->start(data, ttg::structures::make_delegate(this, onFinish));
				}
				static inline void onFinish(SubPipelineExecutor<ProcessInType, InType, OutType, Object> *object,
											std::vector<OutType> *arg)
				{
					//Creating special task.
					ttg::services::WiseTask *task = new ttg::services::WiseTask(0.0);
					task->addKernel(ttg::devices::CPU::x86::getType(),
									new CpuSubPipelineKernel<ProcessInType, OutType>
										(object->owner,
										 object->d,
										 new std::vector<OutType>(*arg),
										 object->cur_ID));
					object->owner->registerTask(task, object->cur_ID, 0);
					//Decing task count.
					NodeState &state = object->owner->getState(object->cur_ID);
					state.getLocker().enter();
					state.getActiveTaskCount() -= 1;
					object->owner->getIContext().getTaskGatherer(object->cur_ID).addTask(task);
					state.getLocker().leave();
					NodeInfo &info = object->owner->getInfo();
					info.getLocker().enter();
					info.getActiveTaskCount() -= 1;
					info.getLocker().leave();
					//Deleting executor.
					SAFE_DELETE(object);
				}
		};
		//Class, that provides process() and sendNext() methods.
		template <class InType, class OutType,
				  class ProcessInType, class ProcessOutType>
		class ProcessorNode :public NavigatorNode,
							 public IDataProcessor<ProcessInType>,
							 public ParametrizedOutput<InType>,
							 public ParametrizedInput<OutType>
		{
			//protected:
			public:
				//Main method, that contains processing logic.
				virtual void process(ttg::pipeline::ContextID, ProcessInType data) = 0;
				//Returns difficulty of given data in abstract measuring units.
				virtual double getDifficulty(ProcessInType data)
				{ return 1; }
				//Sends data to the next nodes.
				inline void sendNext(ttg::structures::Enumerator<ProcessOutType> &en)
				{
					while (en.moveNext())
						getIContext().getDataBuffer().getNextBuffer().push(en.current());
				}
				//Sends data to the next nodes.
				inline void sendNext(ProcessOutType data)
				{ sendNext(ttg::structures::ValueEnumerator<ProcessOutType>(data)); }
				//Sends data to the next nodes.
				inline void sendNext(std::vector<ProcessOutType> &data)
				{
					sendNext(ttg::structures::StlEnumerator<ProcessOutType,
															std::vector<ProcessOutType>::iterator>
									(data.begin(), data.end()));
				}
				//Sends data to the next nodes.
				inline void sendNext(std::list<ProcessOutType> &data)
				{
					sendNext(ttg::structures::StlEnumerator<ProcessOutType,
															std::list<ProcessOutType>::iterator>
									(data.begin(), data.end()));
				}
				//Sends data to this node.
				inline void sendThis(ttg::structures::Enumerator<InType> &en)
				{
					while (en.moveNext())
						getIContext().getDataBuffer().getThisBuffer().push(en.current());
				}
				//Sends data to this node.
				inline void sendThis(InType data)
				{ sendThis(ttg::structures::ValueEnumerator<InType>(data)); }
				//Sends data to this node.
				inline void sendThis(std::vector<InType> &data)
				{
					sendThis(ttg::structures::StlEnumerator<InType,
															std::vector<InType>::iterator>
									(data.begin(), data.end()));
				}
				//Sends data to this node.
				inline void sendThis(std::list<InType> &data)
				{
					sendThis(ttg::structures::StlEnumerator<InType,
															std::list<InType>::iterator>
									(data.begin(), data.end()));
				}
				//Creates interface for SubPipeline, that was created by SubPipeline::addSubPipeline(...).
				//Allows to launch auxiliary pipeline.
				//Returned SubPipeline will valid until owned pipeline recreation and
				//should be released by the user.
				template <class InType_SP, class OutType_SP>
				inline ttg::pipeline::SubPipeline<InType_SP, OutType_SP> createSubPipeline(const char *name)
				{
					ISubPipeline *res;
					if ((res = getIContext().getSubPipeline(name)) != NULL)
						return ttg::pipeline::SubPipeline<InType_SP, OutType_SP>(res);
					else
						throw ttg::Error(ttg::Error::FATAL, "ProcessorNode", "createSubPipeline",
										 "There aren't such SubPipeline");
				}
				//Sends required data to SubPipeline.
				//After performing "method" of current object will be called.
				template <class InType_SP, class OutType_SP>
				inline void sendTo(ttg::pipeline::SubPipeline<InType_SP, OutType_SP> &pipeline,
								   ttg::structures::Enumerator<InType_SP> &data,
								   ttg::structures::Delegate<void, std::vector<OutType_SP> &> *d = NULL)
				{
					//Checking.
					if (pipeline.getSubPipeline() == NULL)
						throw ttg::Error(ttg::Error::FATAL, "ProcessorNode", "sendTo",
										 "SubPipeline wasn't inited");
					//It's not a memory leak. Object will be released after subpipeline launch.
					new SubPipelineExecutor<ProcessInType,
										    InType_SP,
											OutType_SP,
											ProcessorNode<InType, OutType,
														  ProcessInType, ProcessOutType>>
												   (pipeline.getSubPipeline(), data,
													this, d, getIContext().getDataBuffer().getContextID());
				}
				//Sends required data to SubPipeline.
				//After performing "method" of current object will be called.
				template <class InType_SP, class OutType_SP>
				inline void sendTo(ttg::pipeline::SubPipeline<InType_SP, OutType_SP> &pipeline,
								   std::vector<InType_SP> &data,
								   ttg::structures::Delegate<void, std::vector<OutType_SP> &> *d = NULL)
				{
					sendTo<InType_SP, OutType_SP>
						  (pipeline,
						   ttg::structures::StlEnumerator<InType_SP, std::vector<InType_SP>::iterator>(
									data.begin(), data.end()),
						   d);
				}
				//Sends required data to SubPipeline.
				//After performing "method" of current object will be called.
				template <class InType_SP, class OutType_SP>
				inline void sendTo(ttg::pipeline::SubPipeline<InType_SP, OutType_SP> &pipeline,
								   InType_SP data,
								   ttg::structures::Delegate<void, std::vector<OutType_SP> &> *d = NULL)
				{
					ttg::structures::ValueEnumerator<InType_SP> en(data);
					sendTo<InType_SP, OutType_SP>(pipeline, en, d);
				}
			public:
				inline ProcessorNode(const char *name, bool is_serial)
					:NavigatorNode(name, is_serial)
				{
					this->setNavigatorForInput(this);
					this->setNavigatorForOutput(this);
				}
				//Registers task (adds requred handlers and updates node state).
				virtual void registerTask(ttg::services::WiseTask *task,
										  ttg::pipeline::ContextID ID,
										  double abstract_difficulty) = 0;
		};
	}
}

#endif
